﻿// ar8030_test.cpp : This file contains the 'main' function. Program execution begins and ends there.
//


#include <stdio.h>
#include "bb_api.h"
#include "getopt.h"
#include <windows.h>

uint64_t timestamp()
{
    FILETIME ft;
    GetSystemTimeAsFileTime(&ft);
    uint64_t t = ((uint64_t)ft.dwHighDateTime << 32) | ft.dwLowDateTime;
    return (t / 10 - 11644473600000000);
}

static void plot_callback(void* arg, void* user)
{
    bb_event_plot_data_t* data = (bb_event_plot_data_t*)arg;

    printf("user %u:\n", data->user);
    for (int i = 0; i < data->plot_num; i++) {
        bb_plot_data_t* item = &data->plot_data[i];
        printf("SNR: %u  LDPC: %u/%u GAIN:(%u, %u) MCS: %d FCH: %u\n", item->snr, item->ldpc_err, item->ldpc_num, item->gain_a, item->gain_b, item->mcs_rx, item->fch_lock);
    }
}

static int bb_mac_string(const bb_mac_t* mac, char* buf)
{
    sprintf(buf, "%02X%02X%02X%02X", mac->addr[0], mac->addr[1], mac->addr[2], mac->addr[3]);
    return 0;
}

static void bb_evt_tx_len_reset(int sockfd)
{
    bb_socket_ioctl(sockfd, BB_SOCK_TX_LEN_RESET, NULL);
    printf("bb_evt_tx_len_reset\n");
}

static void bb_evt_tx_len_get(int sockfd)
{
    uint8_t buf[2048];
    for (int i = 0; i < sizeof(buf); i++) {
        buf[i] = i;
    }

    int n = 0;

    while (1) {
        int len = bb_socket_write(sockfd, buf, sizeof(buf), -1);
        if (len < 0) {
            break;
        }

        printf("send: %u\n", len);
        Sleep(1000);

        {
            uint64_t buf = 0;
            bb_socket_ioctl(sockfd, BB_SOCK_TX_LEN_GET, &buf);
            printf("bb_evt_tx_len_get %lld\n", buf);

            if (++n >= 10) {
                bb_evt_tx_len_reset(sockfd);
                n = 0;
            }
        }
    }
}

static void evt_callback(void* arg, void* user)
{
    printf("event call back ok\n");
}

static void bb_ioctl_get_1v1_info(bb_dev_handle_t* handle)
{
    bb_get_1v1_info_in_t in = { 0 };
    bb_get_1v1_info_out_t out;
    bb_ioctl(handle, BB_GET_1V1_INFO, &in, &out);
    for (int i = 0; i < 2; i++) {
        bb_info_t* info = (bb_info_t*)&out + i;
        printf("[dev_%d] snr:%d tlv_num_err:%d.%d%% ldpc_num_err:%d.%d%% gain_a:%d gain_b:%d tx_mcs:%d tx_chan:%d\n",
            i, info->snr, info->ldpc_tlv_err_ratio / 100, info->ldpc_tlv_err_ratio % 100,
            info->ldpc_num_err_ratio / 100, info->ldpc_num_err_ratio % 100,
            info->gain_a, info->gain_b, info->tx_mcs - 2, info->tx_chan);
    }
}

static void bb_ioctl_set_master_dev(bb_dev_handle_t* handle, uint8_t port)
{
    bb_set_master_dev_t in;
    in.slot = port;
    bb_ioctl(handle, BB_SET_MASTER_DEV, &in, NULL);
}

static void bb_ioctl_set_mcs_range(bb_dev_handle_t* handle, bb_slot_e slot)
{
    bb_set_mcs_range_in_t in = { 0 };
    in.slot = slot;
    in.mcs_min = BB_PHY_MCS_0;
    in.mcs_max = BB_PHY_MCS_0;
    bb_ioctl(handle, BB_SET_MCS_RANGE, &in, NULL);
    Sleep(10000);

    in.mcs_max = BB_PHY_MCS_6;
    bb_ioctl(handle, BB_SET_MCS_RANGE, &in, NULL);
    Sleep(10000);

    in.mcs_min = BB_PHY_MCS_10;
    in.mcs_max = BB_PHY_MCS_10;
    bb_ioctl(handle, BB_SET_MCS_RANGE, &in, NULL);
    Sleep(10000);

    in.mcs_min = -1;
    in.mcs_max = BB_PHY_MCS_13;
    bb_ioctl(handle, BB_SET_MCS_RANGE, &in, NULL);
}

int main(int argc, char** argv)
{
    int c;
    int flag_help = 0;
    int flag_get_status = 0;
    int flag_get_mcs = 0;
    int flag_flow_tx = 0;
    int flag_flow_rx = 0;
    int flag_pkg_tx = 0;
    int flag_pkg_rx = 0;
    int flag_ping_server = 0;
    int flag_ping_client = 0;
    int flag_plot = 0;
    int flag_pair_slot = -1;
    int flag_usb_id = 0;
    bb_event_e flag_evt_sub = BB_EVENT_MAX;
    bb_sock_cmd_e flag_bb_socket_ioctl = BB_SOCK_IOCTL_MAX;
    uint32_t flag_bb_ioctl = ~0;
    uint32_t port = 0;
    int dev_nr = 0;
    bb_host_t* phost = NULL;
    bb_dev_t** devs = NULL;
    bb_dev_handle_t* handle = NULL;

    while (1) {
        int this_option_optind = optind ? optind : 1;
        int option_index = 0;
        static struct option long_options[] = {
            {"help"                 , no_argument       , 0, 'h'},
            {"get_status"           , no_argument       , 0, 's'},
            {"get_mcs"              , no_argument       , 0, 'm'},
            {"flow_tx"              , no_argument       , 0, 't'},
            {"flow_rx"              , no_argument       , 0, 'r'},
            {"pkg_tx"               , no_argument       , 0, 'D'},
            {"pkg_rx"               , no_argument       , 0, 'd'},
            {"ping_server"          , no_argument       , 0, 'x'},
            {"ping_client"          , no_argument       , 0, 'y'},
            {"port"                 , required_argument , 0, 'p'},
            {"plot"                 , no_argument       , 0, 'P'},
            {"pair"                 , required_argument , 0, 'A'},
            {"socket_ioctl"         , required_argument , 0, 'I'},
            {"ioctl"                , required_argument , 0, 'i'},
            {"usb"                  , required_argument , 0, 'u'},
            {"event"                , required_argument , 0, 'e'},
            {0, 0, 0, 0}
        };

        c = getopt_long(argc, argv, "A:hDdsmrtxyp:PI:i:u:e:", long_options, &option_index);
        if (c == -1) {
            break;
        }

        switch (c) {
        case 'A':
            flag_pair_slot = (int)strtoul(optarg, NULL, 10);
            break;
        case 'h':
            flag_help = 1;
            break;
        case 's':
            flag_get_status = 1;
            break;
        case 'm':
            flag_get_mcs = 1;
            break;
        case 't':
            flag_flow_tx = 1;
            break;
        case 'r':
            flag_flow_rx = 1;
            break;
        case 'x':
            flag_ping_server = 1;
            break;
        case 'y':
            flag_ping_client = 1;
            break;
        case 'p':
            port = strtoul(optarg, NULL, 10);
            break;
        case 'P':
            flag_plot = 1;
            break;
        case 'd':
            flag_pkg_rx = 1;
            break;
        case 'D':
            flag_pkg_tx = 1;
            break;
        case 'I':
            flag_bb_socket_ioctl = (bb_sock_cmd_e)strtoul(optarg, NULL, 10);
            break;
        case 'i':
            flag_bb_ioctl = strtoul(optarg, NULL, 10);
            break;
        case 'u':
            flag_usb_id = (int)strtoul(optarg, NULL, 10);
            break;
        case 'e':
            flag_evt_sub = (bb_event_e)strtoul(optarg, NULL, 10);
            break;
        default:
            printf("?? getopt returned character code 0%o ??\n", c);
            break;
        }
    }

    if (optind < argc) {
        printf("non-option ARGV-elements: ");
        while (optind < argc) {
            printf("%s ", argv[optind++]);
            printf("\n");
        }
    }

    if (flag_help) {
        return 0;
    }

    // 1. 初始化
    // bb_init();

    // 2. 连接daemon
    if (bb_host_connect(&phost, "127.0.0.1", 50000)) {
        printf("failed to connect host!\n");
        goto quit;
    }

    // 3. 获取设备列表
    if ((dev_nr = bb_dev_getlist(phost, &devs)) <= 0) {
        printf("get dev list failed or no dev!\n");
        goto quit;
    }

    if (flag_usb_id >= dev_nr) {
        flag_usb_id = dev_nr - 1;
    }

    // 4. 打开其中一个
    handle = bb_dev_open(devs[flag_usb_id]);

    // 5. 释放列表
    bb_dev_freelist(devs);

    if (!handle) {
        printf("open dev failed!\n");
        goto quit;
    }

    // 配对sample
    if (flag_pair_slot >= 0 && flag_pair_slot < BB_SLOT_MAX) {
        uint32_t count[BB_SLOT_MAX] = { 0 };
        bb_mac_t peer_mac[BB_SLOT_MAX] = { 0 };
        uint8_t slot_bmp = (1 << flag_pair_slot);

        bb_get_status_in_t in;
        bb_get_status_out_t out;
        in.user_bmp = 0;
        bb_ioctl(handle, BB_GET_STATUS, &in, &out);

        printf("pair start slot bmp 0x%02x...\n", slot_bmp);

        // start
        bb_set_pair_mode_t pair_para;
        pair_para.slot_bmp = slot_bmp;
        pair_para.start = 1;
        bb_ioctl(handle, BB_SET_PAIR_MODE, &pair_para, NULL);

        // suppose pair in 3s
        for (int i = 0; i < 150; i++) {
            bb_get_pair_out_t pair_out = { 0 };
            bb_ioctl(handle, BB_GET_PAIR_RESULT, NULL, &pair_out);
            if (pair_out.slot_bmp) {
                for (uint8_t j = 0; j < BB_SLOT_MAX; j++) {
                    if (!(pair_out.slot_bmp & (1 << j))) {
                        continue;
                    }

                    if (memcmp(&peer_mac[j], &pair_out.peer_mac[j], sizeof(bb_mac_t))) {
                        memcpy(&peer_mac[j], &pair_out.peer_mac[j], sizeof(bb_mac_t));
                        count[j] = 1;
                    } else {
                        count[j]++;
                    }
                }
            }

            Sleep(20);
        }

        // stop
        pair_para.start = 0;
        bb_ioctl(handle, BB_SET_PAIR_MODE, &pair_para, NULL);

        // output result and activate the mac
        for (int i = 0; i < BB_SLOT_MAX; i++) {
            if (count[i] > 3) {
                char buf[32];
                bb_mac_string(&peer_mac[i], buf);
                printf("Found peer mac %s in slot %u\n", buf, i);
                if (out.role == BB_ROLE_AP) {
                    bb_set_candidate_t candi;
                    candi.slot = i;
                    memcpy(candi.mac_tab, &peer_mac[i], sizeof(bb_mac_t));
                    candi.mac_num = 1;
                    bb_ioctl(handle, BB_SET_CANDIDATES, &candi, NULL);
                } else {
                    bb_set_ap_mac_t ap_mac;
                    memcpy(&ap_mac.mac, &peer_mac[i], sizeof(bb_mac_t));
                    bb_ioctl(handle, BB_SET_AP_MAC, &ap_mac, NULL);
                }
            }
        }

        printf("pair stop\n");

        goto quit;
    }

    if (flag_evt_sub < BB_EVENT_MAX) {
        bb_set_event_callback_t callback;
        callback.event = flag_evt_sub;
        callback.callback = evt_callback;
        callback.user = 0;
        bb_ioctl(handle, BB_SET_EVENT_SUBSCRIBE, &callback, NULL);

        while (1) {
            Sleep(1000);
        }
        goto quit;
    }

    if (flag_plot) {
        bb_set_event_callback_t callback;
        callback.event = BB_EVENT_PLOT_DATA;
        callback.callback = plot_callback;
        callback.user = 0;
        bb_ioctl(handle, BB_SET_EVENT_SUBSCRIBE, &callback, NULL);

        bb_set_plot_t plot;
        plot.user = BB_USER_0;
        plot.enable = 1;
        plot.cache_num = 3;
        bb_ioctl(handle, BB_SET_PLOT, &plot, NULL);

        Sleep(5000);

        plot.enable = 0;
        bb_ioctl(handle, BB_SET_PLOT, &plot, NULL);

        Sleep(30000);

        goto quit;
    }

    if (flag_get_status) {
        // API BB_GET_STATUS
        bb_get_status_in_t get_status_in;
        bb_get_status_out_t get_status_out;
        get_status_in.user_bmp = 0x3ff;
        bb_ioctl(handle, BB_GET_STATUS, &get_status_in, &get_status_out);
        goto quit;
    }

    if (flag_get_mcs) {
        // API BB_GET_MCS
        bb_get_mcs_in_t get_mcs_in;
        bb_get_mcs_out_t get_mcs_out;
        get_mcs_in.dir = BB_DIR_RX;
        get_mcs_in.slot = BB_SLOT_AP;
        bb_ioctl(handle, BB_GET_MCS, &get_mcs_in, &get_mcs_out);
        goto quit;
    }

    if (flag_ping_server) {
        uint8_t buf[1024];
        int sockfd = bb_socket_open(handle, BB_SLOT_0, port, BB_SOCK_FLAG_TX | BB_SOCK_FLAG_RX, NULL);
        while (1) {
            int len = bb_socket_read(sockfd, buf, sizeof(buf), -1);
            if (len < 0) {
                break;
            }

            if (len == 8) {
                bb_socket_write(sockfd, buf, 8, -1);
            }
        }
        bb_socket_close(sockfd);
        goto quit;
    }

    if (flag_ping_client) {
        uint8_t buf[1024];
        int sockfd = bb_socket_open(handle, BB_SLOT_0, port, BB_SOCK_FLAG_TX | BB_SOCK_FLAG_RX, NULL);
        while (1) {
            uint64_t ts = timestamp();
            int len = bb_socket_write(sockfd, &ts, sizeof(ts), -1);
            if (len < 0) {
                break;
            }

            len = bb_socket_read(sockfd, buf, sizeof(buf), -1);
            if (len != 8) {
                break;
            }

            printf("duration - %u\n", (uint32_t)((timestamp() - *(uint64_t*)buf)));
        }
        bb_socket_close(sockfd);
        goto quit;
    }

    if (flag_flow_tx) {
        uint8_t buf[2048];
        for (int i = 0; i < sizeof(buf); i++) {
            buf[i] = i;
        }

        int sockfd = bb_socket_open(handle, BB_SLOT_0, port, BB_SOCK_FLAG_TX | BB_SOCK_FLAG_RX, NULL);
        while (1) {
            int len = bb_socket_write(sockfd, buf, sizeof(buf), -1);
            if (len < 0) {
                break;
            }

            printf("send: %u\n", len);
            //Sleep(1);
        }
        bb_socket_close(sockfd);
        goto quit;
    }

    if (flag_flow_rx) {
        int sockfd = bb_socket_open(handle, BB_SLOT_0, port, BB_SOCK_FLAG_TX | BB_SOCK_FLAG_RX, NULL);
        while (1) {
            uint8_t buf[2048];
            static uint8_t expected = 0;
            int len = bb_socket_read(sockfd, buf, sizeof(buf), -1);
            if (len < 0) {
                break;
            }
#if 0
            for (int i = 0; i < len; i++) {
                printf("%02X ", buf[i]);
            }
            printf("\n\n");
#endif
            for (int i = 0; i < len; i++) {
                if (buf[i] != expected) {
                    expected = buf[i];
                    printf("Data not expected!\n");
                }
                expected++;
            }

            printf("%u\n", len);
        }
        bb_socket_close(sockfd);
        goto quit;
    }

    if (flag_pkg_rx) {
        int sockfd = bb_socket_open(handle, BB_SLOT_0, port, BB_SOCK_FLAG_RX | BB_SOCK_FLAG_DATAGRAM, NULL);
        while (1) {
            uint8_t buf[2048];

            int len = bb_socket_read(sockfd, buf, sizeof(buf), -1);
            if (len < 0) {
                break;
            }

            printf("%u\n", len);
            int i = 0;
            for (; i < len; i++) {
                if (i != buf[i]) {
                    printf("%u != %u\n", i, buf[i]);
                    break;
                }
            }
        }
        bb_socket_close(sockfd);
        goto quit;
    }

    if (flag_pkg_tx) {
        uint8_t buf[2048];
        for (int i = 0; i < sizeof(buf); i++) {
            buf[i] = i;
        }

        int sockfd = bb_socket_open(handle, BB_SLOT_0, port, BB_SOCK_FLAG_TX | BB_SOCK_FLAG_DATAGRAM, NULL);
        int i = 1;
        while (1) {
            int len = bb_socket_write(sockfd, buf, i, -1);
            if (len < 0) {
                break;
            }

            printf("send: %u\n", len);
            ++i;
            if (i > 256) {
                i = 1;
            }
            Sleep(500);
        }
        bb_socket_close(sockfd);
        goto quit;
    }

    if (flag_bb_ioctl != ~0) {
        printf("bb_ioctl: handle cmd %d, port %d\n", flag_bb_ioctl, port);

        switch (flag_bb_ioctl) {
        case BB_GET_1V1_INFO    : bb_ioctl_get_1v1_info     (handle                     ); break;
        case BB_SET_MASTER_DEV  : bb_ioctl_set_master_dev   (handle, port               ); break;
        case BB_SET_MCS_RANGE   : bb_ioctl_set_mcs_range    (handle, (bb_slot_e)port    ); break;
        default: printf("bb_ioctl err: unsupport cmd %d\n", flag_bb_ioctl); break;
        }
    }

    if (flag_bb_socket_ioctl < BB_SOCK_IOCTL_MAX) {
        printf("bb_socket_ioctl: handle cmd %d, port %d\n", flag_bb_socket_ioctl, port);

        int sockfd = bb_socket_open(handle, BB_SLOT_0, port, BB_SOCK_FLAG_TX | BB_SOCK_FLAG_RX, NULL);
        switch (flag_bb_socket_ioctl) {
        case BB_SOCK_TX_LEN_GET  :   bb_evt_tx_len_get(sockfd)  ; break;
        case BB_SOCK_TX_LEN_RESET:   bb_evt_tx_len_reset(sockfd); break;
        case BB_SOCK_IOCTL_ECHO  :   while (1) Sleep(1000); break;
        default: printf("bb_socket_ioctl err: unsupport cmd %d\n", flag_bb_socket_ioctl); break;
        }
        bb_socket_close(sockfd);
        goto quit;
    }

quit:
    if (handle) {
        bb_dev_close(handle);
    }

    if (phost) {
        bb_host_disconnect(phost);
    }

    // bb_deinit();

    return 0;
}

